﻿using System;
using System.IO;
using System.Windows.Forms;
using DevelopersTestPad.Remoting;
using Ooaavee.Scripting;
using Ooaavee.Support;

namespace DevelopersTestPad.AppFrame
{
    internal sealed class AppContext : IDisposable
    {
        private ScriptingContext _previousScriptingContext;
        private static AppContext _current;

        /// <summary>
        /// The working directory for this app instance.
        /// </summary>
        private DirectoryInfo _workingDirectory;

        /// <summary>
        /// The main application window.
        /// </summary>
        public Form MainWindow { set; get; }

        /// <summary>
        /// Occurs when the 'New Editor Page' command has been requested.
        /// </summary>
        public event EventHandler<AppCommandEventArgs> NewEditorPageRequest;

        /// <summary>
        /// Occurs when the 'Open File' command has been requested.
        /// </summary>
        public event EventHandler<AppCommandEventArgs> OpenFileRequest;

        /// <summary>
        /// Occurs when the 'Save File' command has been requested.
        /// </summary>
        public event EventHandler<AppCommandEventArgs> SaveFileRequest;

        /// <summary>
        /// Occurs when the 'Save File As' command has been requested.
        /// </summary>
        public event EventHandler<AppCommandEventArgs> SaveFileAsRequest;

        /// <summary>
        /// Occurs when the 'Exit Application' command has been requested.
        /// </summary>
        public event EventHandler<AppCommandEventArgs> ExitApplicationRequest;

        /// <summary>
        /// Occurs when the 'Execute' command has been requested.
        /// </summary>
        public event EventHandler<AppCommandEventArgs> ExecuteRequest;

        /// <summary>
        /// Occurs when the 'Show Options' command has been requested.
        /// </summary>
        public event EventHandler<AppCommandEventArgs> ShowOptionsRequest;

        /// <summary>
        /// Occurs when the 'Show About' command has been requested.
        /// </summary>
        public event EventHandler<AppCommandEventArgs> ShowAboutRequest;

        /// <summary>
        /// Occurs when received an app notification.
        /// </summary>
        public event EventHandler<AppNotificationEventArgs> AppNotificationReceived;
 
        /// <summary>
        /// No public constructor available, use Current property.
        /// </summary>
        private AppContext()
        {
            AppNotificationReceived += AppContext_AppNotificationReceived;
        }

        /// <summary>
        /// Gets the current gui context.
        /// </summary>
        public static AppContext Current
        {
            get
            {
                if (_current == null)
                {
                    _current = new AppContext();
                    _current.Initialize();
                }
                return _current;
            }
        }

        /// <summary>
        /// Raises the NewEditorPageRequest event.
        /// </summary>
        /// <param name="e">An <see cref="AppCommandEventArgs"/> that contains the event data.</param>
        public void OnNewEditorPageRequest(AppCommandEventArgs e)
        {            
            OnEvent(NewEditorPageRequest, e);
        }

        /// <summary>
        /// Raises the OpenFileRequest event.
        /// </summary>
        /// <param name="e">An <see cref="AppCommandEventArgs"/> that contains the event data.</param>
        public void OnOpenFileRequest(AppCommandEventArgs e)
        {
            OnEvent(OpenFileRequest, e);
        }

        /// <summary>
        /// Raises the X event.
        /// </summary>
        /// <param name="e">An <see cref="AppCommandEventArgs"/> that contains the event data.</param>
        public void OnSaveFileRequest(AppCommandEventArgs e)
        {
            OnEvent(SaveFileRequest, e);
        }

        /// <summary>
        /// Raises the SaveFileAsRequest event.
        /// </summary>
        /// <param name="e">An <see cref="AppCommandEventArgs"/> that contains the event data.</param>
        public void OnSaveFileAsRequest(AppCommandEventArgs e)
        {
            OnEvent(SaveFileAsRequest, e);
        }

        /// <summary>
        /// Raises the ExitApplicationRequest event.
        /// </summary>
        /// <param name="e">An <see cref="AppCommandEventArgs"/> that contains the event data.</param>
        public void OnExitApplicationRequest(AppCommandEventArgs e)
        {
            OnEvent(ExitApplicationRequest, e);
        }

        /// <summary>
        /// Raises the ExecuteRequest event.
        /// </summary>
        /// <param name="e">An <see cref="AppCommandEventArgs"/> that contains the event data.</param>
        public void OnExecuteRequest(AppCommandEventArgs e)
        {
            OnEvent(ExecuteRequest, e);
        }

        /// <summary>
        /// Raises the ShowOptionsRequest event.
        /// </summary>
        /// <param name="e">An <see cref="AppCommandEventArgs"/> that contains the event data.</param>
        public void OnShowOptionsRequest(AppCommandEventArgs e)
        {
            OnEvent(ShowOptionsRequest, e);
        }

        /// <summary>
        /// Raises the ShowAboutRequest event.
        /// </summary>
        /// <param name="e">An <see cref="AppCommandEventArgs"/> that contains the event data.</param>
        public void OnShowAboutRequest(AppCommandEventArgs e)
        {
            OnEvent(ShowAboutRequest, e);
        }

        /// <summary>
        /// Raises the AppNotificationReceived event.
        /// </summary>
        /// <param name="e">An <see cref="AppNotificationEventArgs"/> that contains the event data.</param>
        public void OnAppNotificationReceived(AppNotificationEventArgs e)
        {
            OnEvent(AppNotificationReceived, e);            
        }

        public void Dispose()
        {
            try
            {
                _workingDirectory.Delete(true);
            }
            catch (Exception)
            {
                // No need to handle this exception
            }
        }

        /// <summary>
        /// Creates a new scripting context for the specified programming language.
        /// </summary>
        /// <param name="language">The programming language.</param>
        /// <returns>The scripting context.</returns>
        public ScriptingContext CreateScriptingContext(Language language)
        {          
            // Use previous scripting context whenever possible.
            if (_previousScriptingContext != null && _previousScriptingContext.Language == language)
            {
                return _previousScriptingContext;
            }

            ScriptingContext scriptingContext = ScriptingContext.CreateDefault();

            scriptingContext.RemoteScriptBaseClassFullName = typeof (DevelopersTestPadRemoteScript).FullName;

            scriptingContext.Language = language;

            scriptingContext.WorkingDirectory = _workingDirectory;

            // Add reference: DevelopersTestPad.Remoting.dll
            Reference r1 = Reference.CreateReference(typeof(DevelopersTestPadRemoteScript).Assembly);
            r1.IsOutsideGac = true;
            scriptingContext.References.Add(r1);

            // Add reference: Ooaavee.Support.dll
            Reference r2 = Reference.CreateReference(typeof(Serializer).Assembly);
            r2.IsOutsideGac = true;
            scriptingContext.References.Add(r2);

            /*
            Assembly a3 = Assembly.LoadFrom(@"D:\Work\Huuhaa\Juuri\Juuri\bin\Release\Apuri.dll");
            Reference r3 = Reference.CreateReference(a3);
            r3.IsOutsideGac = true;
            scriptingContext.References.Add(r3);

            Assembly a4 = Assembly.LoadFrom(@"D:\Work\Huuhaa\Juuri\Juuri\bin\Release\Juuri.dll");
            Reference r4 = Reference.CreateReference(a4);
            r4.IsOutsideGac = true;
            scriptingContext.References.Add(r4);
            */

            _previousScriptingContext = scriptingContext;

            return scriptingContext;
        }

        private void AppContext_AppNotificationReceived(object sender, AppNotificationEventArgs e)
        {
            if (e.Type == AppNotificationType.AppOptionsChanged)
            {
                _previousScriptingContext = null;                
            }
        }

        /// <summary>
        /// Initializes the AppContext.
        /// </summary>
        private void Initialize()
        {
            var temp = new DirectoryInfo(Path.GetTempPath());
            var app = temp.CreateSubdirectory(@"Developer's Test Pad");
            var session = app.CreateSubdirectory(Guid.NewGuid().ToString());

            _workingDirectory = session;
        }

        private void OnEvent(EventHandler<AppCommandEventArgs> handlers, AppCommandEventArgs e)
        {
            if (handlers != null)
            {
                handlers(this, e);
            }
        }

        private void OnEvent(EventHandler<AppNotificationEventArgs> handlers, AppNotificationEventArgs e)
        {
            if (handlers != null)
            {
                handlers(this, e);
            }
        }
    }
}
